﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Universum.Infrastructure.Persistence;
using Universum.Model.Domain.QuestionIT;
using System.Security.Principal;
using Universum.Infrastructure.Persistence.Queries.QuestionIT;
using Universum.Model.Services.Indexing;

namespace Universum.Model.Services.QuestionIt
{
	public class QuestionServiceImpl : IQuestionService
	{
		IRepository _Repository;
		IPrincipal _Owner;
		IQuestionIndexingService _QuestionIndexingService;
		public QuestionServiceImpl(
			IPrincipal owner,
			IRepository repository,
			IQuestionIndexingService questionIndexingService)
		{
			_Owner = owner;
			_Repository = repository;
			_QuestionIndexingService = questionIndexingService;
		}
		#region IQuestionService Members

		public int GetQuestionsCount()
		{
			var result = _Repository.Query<Question>().Count();
			return Math.Max(0, result);
		}

		public IList<Universum.Model.Domain.QuestionIT.Question> GetQuestionsFor(int pageIndex, int resultsPerPage)
		{
			return _Repository.QueryPaged<Question>(pageIndex, resultsPerPage).OrderByDescending(it=>it.Created).ToList();
		}
		public IList<QuestionReply> GetRepliesFor(Guid guid)
		{
			return _Repository.Query(new QuestionReplyByQuestionId(guid)).OrderByDescending(it => it.Created).ToList();
		}
		public Question CreateQuestion()
		{
			return new Question
			{
				ID = Guid.NewGuid(),
				Username = _Owner.Identity.Name,
				Rating = 0,
				Publish = DateTime.Now,
				Created = DateTime.Now,
				Answered = false,
				Closed = false,
				Offensive = false,
				  
			};
		}
				
		public void SaveOrUpdate(Question save)
		{
			_Repository.SaveOrUpdate(save);
			_QuestionIndexingService.Update(save);
		}

		public Question GetQuestionById(Guid guid)
		{
			return _Repository.Load<Question>(guid);
		}

		public Question GetQuestionBySlug(string id)
		{
			return _Repository.Query(new QuestionBySlug(id)).FirstOrDefault();
		}

		

		public QuestionReply GetNewReplyFor(Guid guid)
		{
			return new QuestionReply
			{
				QuestionId = guid	,
				Username = _Owner.Identity.Name	,
				Publish =DateTime.Now,
				Created = DateTime.Now,
				Rating = 0,
				Offensive = false,
				ID = Guid.NewGuid()
			};
		}

		
		public void SaveOrUpdateReply(QuestionReply reply)
		{
			
			_Repository.SaveOrUpdate(reply);
			_QuestionIndexingService.Update(reply);
		}

		public QuestionReply GetQuestionReplyById(Guid id)
		{
			return _Repository.Load<QuestionReply>(id);
		}

		public void RemoveQuestionReply(QuestionReply questionReply)
		{
			_Repository.Remove<QuestionReply>(questionReply);
			_QuestionIndexingService.Remove(questionReply);
		}

		public void RemoveQuestion(Question question)
		{
			var replies =  GetRepliesFor(question.ID);
			foreach (var reply in replies)
			{
				_Repository.Remove(reply);
				_QuestionIndexingService.Remove(reply);
			}
			_Repository.Remove(question);
			_QuestionIndexingService.Remove(question);
		}

		public void MarkQuestionAsOffensive(Question question, string byUsername)
		{
			question.Offensive = true;
			handle_offensive_question(question, byUsername);
			_Repository.SaveOrUpdate(question);
		}

		private void handle_offensive_question(Question question, string byUsername)
		{
			if (question.ConsideredOffensiveBy == null)
			{
				question.ConsideredOffensiveBy = new List<OffensiveQuestion>();
			}
			if (question.ConsideredOffensiveBy.Where(it => it.Username == byUsername).Count() > 0)
			{
				question.ConsideredOffensiveBy.Where(it => it.Username == byUsername).ToList().ForEach(it =>
					question.ConsideredOffensiveBy.Remove(it));
			}
			else
			{
				question.ConsideredOffensiveBy.Add(
					new OffensiveQuestion
					{
						Username=byUsername,
						QuestionId = question.ID,
						 ID = Guid.NewGuid()
					});
			}
		}

		public void UnMarkQuestionAsOffensive(Question question, string byUsername)
		{
			question.Offensive = false;
			handle_offensive_question(question, byUsername);
			_Repository.SaveOrUpdate(question);
		}


		public void UnMarkQuestionReplyAsOffensive(QuestionReply reply)
		{
			reply.Offensive = false;
			_Repository.SaveOrUpdate(reply);
		}

		public void MarkQuestionReplyAsOffensive(QuestionReply reply)
		{
			reply.Offensive = true;
			_Repository.SaveOrUpdate(reply);
		}

		public void MarkQuestionAsAnswered(Question question)
		{
			question.Answered = true;
			_Repository.SaveOrUpdate(question);
		}

		public void MarkQuestionReplyAsAnswer(QuestionReply reply)
		{
			
			reply.IsAnswer = true;
			_Repository.SaveOrUpdate(reply);
		}

		
		public void UnMarkQuestionAsAnswered(Question question)
		{
			var replies = _Repository.Query(new Universum.Infrastructure.Persistence.Queries.QuestionIT.QuestionReplyByQuestionId(question.ID)).ToList();
			
			question.Answered = false;
			replies.ForEach(item =>
				{
					if (item.IsAnswer)
						question.Answered = true;
				});
			

			_Repository.SaveOrUpdate(question);
		}

		public void UnMarkQuestionReplyAsAnswer(QuestionReply reply)
		{
			reply.IsAnswer = false;
			_Repository.SaveOrUpdate(reply);
		}

		#endregion

		#region IQuestionService Members


		public void PlusRatingOnQuestion(Guid id, string byUsername)
		{
			var q = _Repository.Load<Question>(id);
			
			if( q != null){
				handle_voted_by(id, byUsername, q,+1);
				q.Rating = q.Rating + 1;
				_Repository.SaveOrUpdate(q);
				}
		}

		public void MinusRatingOnQuestion(Guid id, string byUsername)
		{
			var q = _Repository.Load<Question>(id);
			
			if (q != null)
			{
				handle_voted_by(id, byUsername, q, -1);
				q.Rating = q.Rating - 1;
				_Repository.SaveOrUpdate(q);
			}
		}

		private static void handle_voted_by(Guid id, string byUsername, Question q,int points)
		{
			if (q.VotedBy == null)
			{
				q.VotedBy = new List<VoteQuestion>();

			}
			if (q.VotedBy.Where(it => it.Username == byUsername).Count() == 0)
			{
				q.VotedBy.Add(new VoteQuestion { Points=points, QuestionId = id, Username = byUsername, ID = Guid.NewGuid() });
			}
			else
			{
				q.VotedBy.Where(it => it.Username == byUsername).ToList().ForEach(it => q.VotedBy.Remove(it));
			}
			
		}

		#endregion

		#region IQuestionService Members


		public IList<Question> GetUnansweredQuestions(int startIndex, int questionsPerPage)
		{
			return _Repository.Query<Question>(new UnansweredQuestions()).OrderByDescending(
				e => e.Publish).
				Skip(startIndex * questionsPerPage)
				.Take(questionsPerPage).ToList();
		}

		public IList<Question> GetHottestQuestions(int startIndex, int questionsPerPage)
		{
			return _Repository.Query<Question>()
				.OrderByDescending(
				e => e.Rating).
				Skip(startIndex * questionsPerPage)
				.Take(questionsPerPage).ToList();
		}

		public IList<Question> GetAnsweredQuestions(int startIndex, int questionsPerPage)
		{
			return _Repository.Query<Question>()
				.Where(e=>e.Answered == true)
				.OrderByDescending(e => e.Publish)				
				.Skip(startIndex * questionsPerPage)
				.Take(questionsPerPage).ToList();
		}

		#endregion

		#region IQuestionService Members


		public IList<Question> GetOffensiveQuestionsFor(int startIndex, int questionsPerPage)
		{
			return _Repository.Query<Question>()
				.Where(it=>it.Offensive==true)
				.OrderBy(
				e => e.Rating).
				Skip(startIndex * questionsPerPage)
				.Take(questionsPerPage).ToList();
		}

		#endregion

		#region IQuestionService Members


		public void UnMarkQuestionAsOffensiveBySuperUser(Guid id)
		{
			var question = _Repository.Load<Question>(id);
			question.Offensive = false;
			question.ConsideredOffensiveBy.Clear();
			_Repository.SaveOrUpdate(question);
		}

		#endregion
	}
}
